/**
 * Copyright (c) 2015-2017, Henry Yang 杨勇 (gismail@foxmail.com).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.lambkit.db;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.jfinal.kit.PathKit;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.DbPro;
import com.jfinal.plugin.activerecord.Model;
import com.lambkit.cache.CacheManager;
import com.lambkit.core.Lambkit;
import com.lambkit.core.LambkitConfig;
import com.lambkit.core.exception.LambkitIllegalConfigException;
import com.lambkit.dao.record.LambkitRecord;
import com.lambkit.db.datasource.DataSourceConfig;
import com.lambkit.plugin.activerecord.ActiveRecordCache;
import com.lambkit.plugin.activerecord.datasource.DataSourcePluginBuilder;
import com.lambkit.plugin.activerecord.dialect.*;

import java.util.List;

public class LambkitDataSource {

	/**
	 * 数据源名称
	 */
	private String configName; // DataSourceConfig Name from DataSourceConfigManager
	/**
	 * 数据源运行插件
	 */
	private DataSourcePlugin dataSourcePlugin;
	/**
	 * 数据源初始化插件
	 */
	private DataSourceInitPlugin dataSourceInitPlugin;
	/**
	 * 数据表
	 */
	private List<LambkitTableMapping> tables;
	/**
	 * 数据库配置
	 */
	private DataSourceConfig config;
	/**
	 * 是否是动态数据源
	 */
	private boolean dynamicDataSource = false;

	/**
	 * 是否启用
	 */
	private boolean started =false;

	public LambkitDataSource(DataSourceConfig datasourceConfig) {
		if (datasourceConfig.isConfigOk()) {
			this.configName = datasourceConfig.getName();
			dataSourcePlugin = createDataSourcePlugin(datasourceConfig);
			dataSourcePlugin.getActiveRecordPlugin().setShowSql(Lambkit.config(LambkitConfig.class).isDevMode());
			dataSourcePlugin.getActiveRecordPlugin().setCache(new ActiveRecordCache());
			configSqlTemplate(datasourceConfig, dataSourcePlugin.getActiveRecordPlugin());
			configDialect(dataSourcePlugin.getActiveRecordPlugin(), datasourceConfig);
		}
		this.config = datasourceConfig;
	}

	public LambkitDataSource(DataSourceConfig datasourceConfig, boolean dynamicDataSource) {
		this(datasourceConfig);
		this.dynamicDataSource = dynamicDataSource;
	}

	public LambkitDataSource(DataSourceConfig datasourceConfig, DataSourcePlugin dataSourcePlugin) {
		this.config = datasourceConfig;
		this.configName = dataSourcePlugin.getConfigName();
		this.dataSourcePlugin = dataSourcePlugin;
		configSqlTemplate(datasourceConfig, dataSourcePlugin.getActiveRecordPlugin());
		configDialect(dataSourcePlugin.getActiveRecordPlugin(), datasourceConfig);
	}

	public DataSourceConfig getDataSourceConfig() {
		return config;
	}

	public DbPro db() {
		try {
			return Db.use(configName);
		} catch (Exception e) {
			start();
		}
		return Db.use(configName);
	}

	/**
	 * 启动插件
	 */
	public boolean start() {
		if(dataSourcePlugin!=null) {
			started = dataSourcePlugin.start();
		}
		return started;
	}

	/**
	 * 停止插件
	 */
	public boolean stop() {
		if(dataSourcePlugin!=null) {
			boolean flag = dataSourcePlugin.stop();
			if(flag) {
				started = false;
			}
			return flag;
		}
		return true;
	}

	/**
	 * 状态
	 * @return
	 */
	public int getDataSourceActiveState() {
		return dataSourcePlugin!=null ? dataSourcePlugin.getDataSourceActiveState() : 0;
	}

	public void addTable(LambkitTableMapping table) {
		if(tables==null) {
			tables = CollUtil.newArrayList();
		}
		tables.add(table);
	}

	public void addMapping(String tableName, String primaryKey, Class<? extends Model<?>> modelClass, Class<? extends LambkitRecord> pojoClass) {
		if(dataSourcePlugin!=null) {
			dataSourcePlugin.addMapping(tableName, primaryKey, modelClass, pojoClass);
		}
	}

	public void addMapping(String tableName, Class<? extends Model<?>> modelClass, Class<? extends LambkitRecord> pojoClass) {
		if(dataSourcePlugin!=null) {
			dataSourcePlugin.addMapping(tableName, modelClass, pojoClass);
		}
	}

	public void addSqlTemplate(ScanJarStringSource scanJarStringSource) {
		if(dataSourcePlugin!=null) {
			dataSourcePlugin.addSqlTemplate(scanJarStringSource);
		}
	}

	public void setShowSql(boolean showSql) {
		if(dataSourcePlugin!=null) {
			dataSourcePlugin.setShowSql(showSql);
		}
	}

	/**
	 * 配置 本地 sql
	 *
	 * @param datasourceConfig
	 * @param activeRecordPlugin
	 */
	private void configSqlTemplate(DataSourceConfig datasourceConfig, ActiveRecordPlugin activeRecordPlugin) {
		String sqlTemplatePath = datasourceConfig.getSqlTemplatePath();
		if (StrUtil.isNotBlank(sqlTemplatePath)) {
			if (sqlTemplatePath.startsWith("/")) {
				activeRecordPlugin.setBaseSqlTemplatePath(datasourceConfig.getSqlTemplatePath());
			} else {
				activeRecordPlugin.setBaseSqlTemplatePath(PathKit.getRootClassPath() + "/" + datasourceConfig.getSqlTemplatePath());
			}
		} else {
			activeRecordPlugin.setBaseSqlTemplatePath(PathKit.getRootClassPath());
		}
		String sqlTemplateString = datasourceConfig.getSqlTemplate();
		if (sqlTemplateString != null) {
			String[] sqlTemplateFiles = sqlTemplateString.split(",");
			for (String sql : sqlTemplateFiles) {
				activeRecordPlugin.addSqlTemplate(sql);
			}
		}
	}

	/**
	 * 配置 数据源的 方言
	 *
	 * @param activeRecordPlugin
	 * @param datasourceConfig
	 */
	private void configDialect(ActiveRecordPlugin activeRecordPlugin, DataSourceConfig datasourceConfig) {
		switch (datasourceConfig.getType()) {
			case DataSourceConfig.TYPE_MYSQL:
				activeRecordPlugin.setDialect(new LambkitMysqlDialect());
				break;
			case DataSourceConfig.TYPE_ORACLE:
				activeRecordPlugin.setDialect(new LambkitOracleDialect());
				break;
			case DataSourceConfig.TYPE_SQLSERVER:
				activeRecordPlugin.setDialect(new LambkitSqlServerDialect());
				break;
			case DataSourceConfig.TYPE_SQLITE:
				activeRecordPlugin.setDialect(new LambkitSqlite3Dialect());
				break;
			case DataSourceConfig.TYPE_ANSISQL:
				activeRecordPlugin.setDialect(new LambkitAnsiSqlDialect());
				break;
			case DataSourceConfig.TYPE_POSTGRESQL:
				activeRecordPlugin.setDialect(new LambkitPostgreSqlDialect());
				break;
			default:
				throw new LambkitIllegalConfigException("only support datasource type : mysql、orcale、sqlserver、sqlite、ansisql and postgresql, please check your lambkit.properties. ");
		}
	}

	/**
	 * 创建 ActiveRecordPlugin 插件，用于数据库读写
	 *
	 * @param config
	 * @return
	 */
	private DataSourcePlugin createDataSourcePlugin(DataSourceConfig config) {
		DataSourcePlugin dataSourcePlugin = new DataSourcePluginBuilder(config).build(this);
//        if(dataSourcePlugin!=null) {
//        	LambkitManager.me().addActiveRecord(new ActiveRecordBean(config.getName(), config.getDbname(), config));
//        }
		/**
		 * 不需要添加映射的直接返回
		 */
		if (!config.isNeedAddMapping()) {
			return dataSourcePlugin;
		}
		System.out.println("需要添加映射");

		String configTableString = config.getTable();
		String excludeTableString = config.getExcludeTable();

		List<LambkitTableMapping> TableMappings = TableMappingManager.me().getTablesInfos(configTableString, excludeTableString);
		if (ArrayUtil.isEmpty(TableMappings)) {
			return dataSourcePlugin;
		}
		for (LambkitTableMapping ti : TableMappings) {
			if (StrUtil.isNotBlank(ti.getPrimaryKey())) {
				dataSourcePlugin.addMapping(ti.getTableName(), ti.getPrimaryKey(), ti.getModelClass(), ti.getPojoClass());
			} else {
				dataSourcePlugin.addMapping(ti.getTableName(), ti.getModelClass(), ti.getPojoClass());
			}
		}
		return dataSourcePlugin;
	}

	public String getConfigName() {
		return configName;
	}
	public void setConfigName(String configName) {
		this.configName = configName;
	}
	public List<LambkitTableMapping> getTables() {
		return tables;
	}
	public void setTables(List<LambkitTableMapping> tables) {
		this.tables = tables;
	}

	public boolean isDynamicDataSource() {
		return dynamicDataSource;
	}

	public void setDynamicDataSource(boolean dynamicDataSource) {
		this.dynamicDataSource = dynamicDataSource;
	}

	public DataSourcePlugin getDataSourcePlugin() {
		return dataSourcePlugin;
	}

	public void setDataSourcePlugin(DataSourcePlugin dataSourcePlugin) {
		this.dataSourcePlugin = dataSourcePlugin;
	}

	public void setInitSql(String tableName, String initSql) {
		if(StrUtil.isBlank(initSql)) {
			return;
		}
		if(dataSourceInitPlugin==null) {
			dataSourceInitPlugin = new DataSourceInitPlugin();
		}
		this.dataSourceInitPlugin.addInitSql(configName, tableName, initSql);
	}

	public void setInitSql(String tableName, ScanJarStringSource scanJarStringSource) {
		if(scanJarStringSource==null) {
			return;
		}
		String initSql = scanJarStringSource.getContent().toString();
		if(StrUtil.isBlank(initSql)) {
			return;
		}
		if(dataSourceInitPlugin==null) {
			dataSourceInitPlugin = new DataSourceInitPlugin();
		}
		this.dataSourceInitPlugin.addInitSql(configName, tableName, initSql);
	}

	public DataSourceInitPlugin getDataSourceInitPlugin() {
		return dataSourceInitPlugin;
	}

	public void setDataSourceInitPlugin(DataSourceInitPlugin dataSourceInitPlugin) {
		this.dataSourceInitPlugin = dataSourceInitPlugin;
	}
}
